package com.gitee.wsl.text.regex

fun sampleRegex(scopeConfig: RegexBuildScope.()->Unit):Regex{
    val scope= RegexBuildScopeImp()
    scopeConfig(scope)
    return scope.build()
}

interface RegexBuildScope{
     var lastGroup: Int

     val regexParts : StringBuilder

    fun anyChar() = addPart(".")

    fun digit() = addPart("\\d")

    fun letter() = addPart("[[:alpha:]]")

    fun alphaNumeric() = addPart("[A-Za-z0-9]")

    fun whitespace() = addPart("\\s")

    fun wordBoundary() = addPart("\\b")

    fun wordCharacter() = addPart("\\w")


    fun addPart(part: String)

    fun addPart(partBean: RegexPart) = addPart(partBean.regexStr())

    fun addWithModifier(s: String, modifier: String) = addPart("(?:$s)$modifier")

    fun literally(s: String) = addPart(Regex.escape(s))

    private fun pattern(block: RegexBuildScope.() -> Unit): String {
        val innerContext = RegexBuildScopeImp(lastGroup)
        innerContext.block()
        lastGroup = innerContext.lastGroup
        return innerContext.regexParts.toString()
    }

    infix fun Int.times(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "{$this}")
    infix fun Int.times(s: String) = this times { literally(s) }

    infix fun IntRange.times(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "{$first,$last}")
    infix fun IntRange.times(s: String) = this times { literally(s) }

    infix fun Int.timesOrMore(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "{$this,}")
    infix fun Int.timesOrMore(s: String) = this timesOrMore { literally(s) }

    infix fun Int.timesOrLess(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "{0,$this}")
    infix fun Int.timesOrLess(s: String) = this timesOrLess { literally(s) }

    fun group(block: RegexBuildScope.() -> Unit): Int {
        val result = ++lastGroup
        addPart("(${pattern(block)})")
        return result
    }

    fun group(name: String, block: RegexBuildScope.() -> Unit): Int {
        val result = ++lastGroup
        addPart("(?<$name>${pattern(block)})")
        return result
    }

    fun matchGroup(index: Int) = addPart("\\$index")

    fun matchGroup(name: String) = addPart("\\k<$name>")

    fun include(regex: Regex) {
        val pattern = regex.pattern
        addPart(pattern)
        lastGroup += regex.find("")?.destructured?.toList()?.size?:0
    }

    fun startOfString() = addPart("^")
    fun endOfString() = addPart("$")

    fun oneOrMore(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "+")
    fun oneOrMore(s: String) = oneOrMore { literally(s) }

    fun optional(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "?")
    fun optional(s: String) = optional { literally(s) }

    fun zeroOrMore(block: RegexBuildScope.() -> Unit) = addWithModifier(pattern(block), "*")
    fun zeroOrMore(s: String) = zeroOrMore { literally(s) }

    fun anyOf(vararg terms: String) = addPart(terms.joinToString("|", "(?:", ")") { Regex.escape(it) })

    fun anyOf(vararg blocks: RegexBuildScope.() -> Unit) =
        addPart(blocks.joinToString("|", "(?:", ")") { pattern(it) })

    fun anyOf(vararg characters: Char) =
        addPart(characters.joinToString("", "[", "]").replace("\\", "\\\\").replace("^", "\\^"))

    fun anyOf(vararg ranges: CharRange) = addPart(ranges.joinToString("", "[", "]") { "${it.first}-${it.last}" })
}

operator fun RegexBuildScope.plusAssign(other: RegexPart) {
    this.addPart(other)
}

operator fun RegexBuildScope.plusAssign(other: String) {
    this.addPart(other)
}

class RegexBuildScopeImp(override var lastGroup: Int=0): RegexBuildScope {

    override val regexParts = StringBuilder()

    override fun addPart(part: String) {
        regexParts.append(part)
    }

    fun build():Regex = regexParts.toString().toRegex()
}